package com.tabjin.rfidsocket.service.sys.impl;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.tabjin.rfidsocket.mapper.mapper.SysAclCustomMapper;
import com.tabjin.rfidsocket.mapper.mapper.SysAclMapper;
import com.tabjin.rfidsocket.mapper.mapper.SysAclModuleMapper;
import com.tabjin.rfidsocket.mapper.mapper.SysDeptMapper;
import com.tabjin.rfidsocket.pojo.SysAcl;
import com.tabjin.rfidsocket.pojo.SysAclModule;
import com.tabjin.rfidsocket.pojo.SysDept;
import com.tabjin.rfidsocket.pojo.dto.AclDTO;
import com.tabjin.rfidsocket.pojo.dto.AclModuleLevelDTO;
import com.tabjin.rfidsocket.pojo.dto.DeptLevelDTO;
import com.tabjin.rfidsocket.pojo.vo.SysAclVO;
import com.tabjin.rfidsocket.service.sys.SysCoreService;
import com.tabjin.rfidsocket.service.sys.SysTreeService;
import com.tabjin.rfidsocket.utils.LevelUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import java.security.acl.Acl;
import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;

/**
 * @author tabjin
 * create at 2020/12/8 14:29
 * @program rfid_socket
 * @description
 */
@Service
public class SysTreeServiceImpl implements SysTreeService {

    private final SysDeptMapper sysDeptMapper;

    private final SysCoreService sysCoreService;

    private final SysAclModuleMapper sysAclModuleMapper;

    private final SysAclMapper sysAclMapper;

    private final SysAclCustomMapper sysAclCustomMapper;

    public SysTreeServiceImpl(SysDeptMapper sysDeptMapper, SysCoreService sysCoreService, SysAclModuleMapper sysAclModuleMapper, SysAclMapper sysAclMapper, SysAclCustomMapper sysAclCustomMapper) {
        this.sysDeptMapper = sysDeptMapper;
        this.sysCoreService = sysCoreService;
        this.sysAclModuleMapper = sysAclModuleMapper;
        this.sysAclMapper = sysAclMapper;
        this.sysAclCustomMapper = sysAclCustomMapper;
    }

    /**
     * 将原始部门数据生成树
     *
     * @return
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public List<DeptLevelDTO> deptTree() {
        List<SysDept> deptList = sysDeptMapper.selectAll();// 原始数据
        List<DeptLevelDTO> dtoList = Lists.newArrayList();
        for (SysDept dept : deptList) {
            DeptLevelDTO dto = DeptLevelDTO.adapt(dept);// 数据传输至dto
            // 前端数据添加label
            dto.setLabel(dto.getName());
            dtoList.add(dto);
        }

        // 将dtoList做成树形结构，也就是将每个dto中的dept
        return deptListToTree(dtoList);
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public List<DeptLevelDTO> deptTree(Integer id) {
        Example example = new Example(SysDept.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("id", id);
        List<SysDept> deptList = sysDeptMapper.selectByExample(example);// 原始数据

        List<SysDept> fullDeptList = sysDeptMapper.selectAll();// 原始数据

        List<DeptLevelDTO> dtoList = Lists.newArrayList();
        List<DeptLevelDTO> fullDtoList = Lists.newArrayList();
        for (SysDept dept : deptList) {
            DeptLevelDTO dto = DeptLevelDTO.adapt(dept);// 数据传输至dto
            // 前端数据添加label
            dto.setLabel(dto.getName());
            dtoList.add(dto);
        }
        for (SysDept dept : fullDeptList) {
            DeptLevelDTO dto = DeptLevelDTO.adapt(dept);// 数据传输至dto
            // 前端数据添加label
            dto.setLabel(dto.getName());
            fullDtoList.add(dto);
        }

        // 将dtoList做成树形结构，也就是将每个dto中的dept
        return deptListToTree(dtoList, fullDtoList);
    }

    /**
     * 递归将dtoList做成树形结构
     * 数据的核心组装，处理完root递归处理下一层
     *
     * @param deptLevelDTOList
     * @return
     */
    public List<DeptLevelDTO> deptListToTree(List<DeptLevelDTO> deptLevelDTOList, List<DeptLevelDTO> fullDeptLevelDTOList) {
        if (CollectionUtils.isEmpty(deptLevelDTOList)) {
            return Lists.newArrayList();
        }
        // 1.这边将Tree以level为key，相同level的部门做成value，然后置入Map
        // level -> [dept1, dept2, dept3, ...]
        Multimap<String, DeptLevelDTO> levelDTOMultimap = ArrayListMultimap.create();
        List<DeptLevelDTO> rootList = Lists.newArrayList();
        for (DeptLevelDTO dto : fullDeptLevelDTOList) {
            levelDTOMultimap.put(dto.getLevel(), dto);// 这样每次都可以将相同level的部门list取出来
            if (LevelUtil.ROOT.equals(dto.getLevel())) {// 若为顶级部门，则将dto将入rootList中
                rootList.add(dto);
            }
        }
        // 以上完成数据结构的初始化

        // 2.根据rootList为起点，以levelDTOMultimap的值作为辅助数据，将rootList返回出来

        // 2.1先要对root进行排序，毕竟同一级部门也是有顺序的，按照seq从小到大进行排序
        Collections.sort(rootList, new Comparator<DeptLevelDTO>() {
            @Override
            public int compare(DeptLevelDTO o1, DeptLevelDTO o2) {
                return o1.getSeq() - o2.getSeq();
            }
        });

        // 2.2递归生成树，将排好序的rootList其下面的值进行递归排序，
        // 相当于每遍历一层都会对该层数据进行一次树形结构的处理，直到下面没有层
        transformDeptTree(rootList, LevelUtil.ROOT, levelDTOMultimap);
        return rootList;
    }

    /**
     * 递归将dtoList做成树形结构
     * 数据的核心组装，处理完root递归处理下一层
     *
     * @param deptLevelDTOList
     * @return
     */
    public List<DeptLevelDTO> deptListToTree(List<DeptLevelDTO> deptLevelDTOList) {
        if (CollectionUtils.isEmpty(deptLevelDTOList)) {
            return Lists.newArrayList();
        }
        // 1.这边将Tree以level为key，相同level的部门做成value，然后置入Map
        // level -> [dept1, dept2, dept3, ...]
        Multimap<String, DeptLevelDTO> levelDTOMultimap = ArrayListMultimap.create();
        List<DeptLevelDTO> rootList = Lists.newArrayList();
        for (DeptLevelDTO dto : deptLevelDTOList) {
            levelDTOMultimap.put(dto.getLevel(), dto);// 这样每次都可以将相同level的部门list取出来
            if (LevelUtil.ROOT.equals(dto.getLevel())) {// 若为顶级部门，则将dto将入rootList中
                rootList.add(dto);
            }
        }
        // 以上完成数据结构的初始化

        // 2.根据rootList为起点，以levelDTOMultimap的值作为辅助数据，将rootList返回出来

        // 2.1先要对root进行排序，毕竟同一级部门也是有顺序的，按照seq从小到大进行排序
        Collections.sort(rootList, new Comparator<DeptLevelDTO>() {
            @Override
            public int compare(DeptLevelDTO o1, DeptLevelDTO o2) {
                return o1.getSeq() - o2.getSeq();
            }
        });

        // 2.2递归生成树，将排好序的rootList其下面的值进行递归排序，
        // 相当于每遍历一层都会对该层数据进行一次树形结构的处理，直到下面没有层
        transformDeptTree(rootList, LevelUtil.ROOT, levelDTOMultimap);
        return rootList;
    }

    /**
     * 递归生成树，每一层都会写到上一层结点中
     * 以ROOT为例，首先遍历rootList，取出每个元素下一级的层级level，
     * 然后同样的如rootList先排序再针对下一层的tempDeptList进行transformDeptTree
     * level : 0 -> 0.1, 0.2
     * level : 0.1
     * level : 0.2
     *
     * @param deptLevelDTOList 当前结构
     * @param level            当前level，这边最开始是以ROOT处理，到了下一层级每次更新
     * @param levelDTOMultimap 存储若干同一级的map，用以辅助计算
     */
    public void transformDeptTree(List<DeptLevelDTO> deptLevelDTOList,
                                  String level,
                                  Multimap<String, DeptLevelDTO> levelDTOMultimap) {
        for (int i = 0; i < deptLevelDTOList.size(); i++) {
            // 遍历当前层的每个元素
            DeptLevelDTO deptLevelDTO = deptLevelDTOList.get(i);
            // 处理当前层级数据
            String nextLevel = LevelUtil.calculateLevel(level, deptLevelDTO.getId());
            // 处理下一层
            List<DeptLevelDTO> tempDeptList = (List<DeptLevelDTO>) levelDTOMultimap.get(nextLevel);
            if (CollectionUtils.isNotEmpty(tempDeptList)) {
                // 排序
                Collections.sort(tempDeptList, deptSeqComparator);
                // 设置下一层部门
                deptLevelDTO.setDeptLevelDTOList(tempDeptList);
                // 前端数据添加children
                deptLevelDTO.setChildren(tempDeptList);
                // 进入到下一层处理
                transformDeptTree(tempDeptList, nextLevel, levelDTOMultimap);
            }
        }
    }

    /**
     * 部门排序规则比较器
     */
    public Comparator<DeptLevelDTO> deptSeqComparator = new Comparator<DeptLevelDTO>() {
        @Override
        public int compare(DeptLevelDTO o1, DeptLevelDTO o2) {
            return o1.getSeq() - o2.getSeq();
        }
    };

    /****************************************************/
    /** 权限树 */
    /****************************************************/

    /**
     * 原始角色数据生成角色权限树
     *
     * @param roleId 角色id
     * @return List<AclModuleLevelDTO>
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public List<AclModuleLevelDTO> roleTree(Integer roleId) {
        // 1.当前用户已分配的权限点
        List<SysAclVO> userAclList = sysCoreService.getCurrentUserAclList();
        // 2.当前角色已分配的权限点
        List<SysAclVO> currentRoleAclList = sysCoreService.getCurrentRoleAclList(roleId);
        // 3.当前系统所有的权限点
        // 生成一个列表，包含角色对应权限相关的标记
        List<AclDTO> aclDTOList = Lists.newArrayList();
        // 4.将用户和角色相关的权限点id做成set
        // 存放取出的用户的权限id
        // 将取出的用户已分配的权限点的所有 id 构成一个 set
        Set<Integer> userAclIdSet = userAclList.stream().map(sysAclVO -> sysAclVO.getId()).collect(Collectors.toSet());
        Set<Integer> roleAclIdSet = currentRoleAclList.stream().map(sysAclVO -> sysAclVO.getId()).collect(Collectors.toSet());
        // 获取二者的并集
        List<SysAclVO> allSysAclList = sysAclCustomMapper.getAll();// 获取所有的权限点（不针对用户）
//        Set<SysAclVO> aclVOSet = new HashSet<>(allSysAclList);// 下面遍历的时候就是遍历整个权限集合
//        Set<SysAclVO> aclVOSet = new HashSet<SysAclVO>(currentRoleAclList);
//        aclVOSet.addAll(userAclList);// 先将二者合并，然后就可以一次性遍历完userAclIdList和roleAclIdList
        // 注意要在SysAclVO上添加@EqualsAndHashCode(of = {"id"})注解，这样上面addAll在id相同的值就不会有多个

        // 5.遍历整个 系统权限点 来判断当前的权限点在当前用户是否有权限访问，以及是否需要默认选中
//        for (SysAclVO sysAclVO : aclVOSet) { // 这边调整，不用aclVOSet，
        for (SysAclVO sysAclVO : allSysAclList) {
            // TODO 这边其实是多余的
            SysAcl sysAcl = new SysAcl();
            BeanUtils.copyProperties(sysAclVO, sysAcl);
            // TODO 这边其实是多余的

            AclDTO aclDTO = AclDTO.adapt(sysAcl);
            // 根据用户权限关系确定用户是否有权限
            if (userAclIdSet.contains(aclDTO.getId())) {
                aclDTO.setHasAcl(true);
            }
            // 根据角色权限关系确定该用户的权限点是否被选中
            if (roleAclIdSet.contains(aclDTO.getId())) {
                aclDTO.setChecked(true);
            }
            aclDTOList.add(aclDTO);
        }
        // 6.将aclDTOList转换为权限模块和权限点组成的树
        return aclListToTree(aclDTOList);
    }

    /**
     * 权限模块树
     *
     * @return List<AclModuleLevelDTO>
     */
    @Override
    public List<AclModuleLevelDTO> aclModuleTree() {
        List<SysAclModule> aclModuleList = sysAclModuleMapper.selectAll();
        List<AclModuleLevelDTO> dtoList = Lists.newArrayList();
        for (SysAclModule aclModule : aclModuleList) {
            dtoList.add(AclModuleLevelDTO.adapt(aclModule));
        }
        return aclModuleListToTree(dtoList);
    }

    /**
     * 当前用户已有权限组成的树
     *
     * @param userId
     * @return List<AclModuleLevelDTO>
     */
    @Override
    public List<AclModuleLevelDTO> userAclTree(Integer userId) {
        // 1.当前用户已分配的权限点
        List<SysAclVO> currentUserAclList = sysCoreService.getUserAclList(userId);
        // 2.直接做成aclDTOList
        List<AclDTO> aclDTOList = Lists.newArrayList();
        for (SysAclVO sysAclVO : currentUserAclList) {
            // TODO 这边其实是多余的
            SysAcl sysAcl = new SysAcl();
            BeanUtils.copyProperties(sysAclVO, sysAcl);
            // TODO 这边其实是多余的
            AclDTO dto = AclDTO.adapt(sysAcl);
            // 这边因为所有的权限是用户拥有的权限
            dto.setHasAcl(true);
            dto.setChecked(true);
            aclDTOList.add(dto);
        }
        return aclListToTree(aclDTOList);
    }

    @Override
    public Set<String> userAclNamesByUserId(Integer userId) {
        List<SysAclVO> userAclList = sysCoreService.getUserAclList(userId);
        Set<SysAclVO> sysAclVOSet = new HashSet<>(userAclList);
        Set<String> userAclNames = new HashSet<>();
        Iterator it = sysAclVOSet.iterator();
        while (it.hasNext()) {
            it.next();
        }
        return null;
    }

    /**
     * 递归将aclDTOList转换为权限模块和权限点组成的树
     * 数据的核心组装，处理完root递归处理下一层
     *
     * @param aclDTOList
     * @return
     */
    public List<AclModuleLevelDTO> aclListToTree(List<AclDTO> aclDTOList) {
        if (CollectionUtils.isEmpty(aclDTOList)) {
            return Lists.newArrayList();
        }
        // 调用之前生成的权限模块树 -> 权限模块列表
        List<AclModuleLevelDTO> aclModuleLevelList = aclModuleTree();

        // 初始化每个权限模块id和下面对应的权限点列表
        // 此即当前系统中所有有效权限点构成的Map
        // 1.每个权限模块下的权限点列表
        Multimap<Integer, AclDTO> moduleIdAclMap = ArrayListMultimap.create();
        for (AclDTO aclDTO : aclDTOList) {
            if (aclDTO.getStatus() == 1) {// 权限状态有效进入处理流程
                moduleIdAclMap.put(aclDTO.getAclModuleId(), aclDTO);
            }
        }
        // 2.递归遍历权限模块树，将权限点绑定到指定的权限模块上
        bindAclsToTreeWithOrder(aclModuleLevelList, moduleIdAclMap);
        return aclModuleLevelList;// 已经排好序的权限点的集合
    }

    /**
     * 将权限点绑定到权限模块树上
     * 递归遍历每一层模块，每一层取出当前模块下的所有权限点，排完序之后绑定到权限模块上
     *
     * @param aclModuleLevelDTOList 权限模块层级列表
     * @param moduleIdAclMap        权限模块id Map
     */
    public void bindAclsToTreeWithOrder(List<AclModuleLevelDTO> aclModuleLevelDTOList,
                                        Multimap<Integer, AclDTO> moduleIdAclMap) {
        if (CollectionUtils.isEmpty(aclModuleLevelDTOList)) {
            return;
        }
        // 遍历当前层级，并取出当前层级的所有权限点
        for (AclModuleLevelDTO dto : aclModuleLevelDTOList) {
            // 根据模块id选出权限点列表，并且是有效的
            List<AclDTO> aclDTOList = (List<AclDTO>) moduleIdAclMap.get(dto.getId());
            if (CollectionUtils.isNotEmpty(aclDTOList)) {
                // 将权限点绑定到权限模块上
                Collections.sort(aclDTOList, aclSeqComparator);// 排序
                dto.setAclDTOList(aclDTOList);
            }
            // 针对下一层级进行绑定操作
            bindAclsToTreeWithOrder(dto.getAclModuleLevelDTOList(), moduleIdAclMap);
        }
    }

    /**
     * @param dtoList
     * @return
     */
    public List<AclModuleLevelDTO> aclModuleListToTree(List<AclModuleLevelDTO> dtoList) {
        if (CollectionUtils.isEmpty(dtoList)) {
            return Lists.newArrayList();
        }
        // level -> [aclModule1, aclModule2, aclModule3, ...]  Map<String, List<Object>>
        Multimap<String, AclModuleLevelDTO> levelDTOMultimap = ArrayListMultimap.create();
        List<AclModuleLevelDTO> rootList = Lists.newArrayList();

        for (AclModuleLevelDTO dto : dtoList) {
            levelDTOMultimap.put(dto.getLevel(), dto);
            if (LevelUtil.ROOT.equals(dto.getLevel())) {
                rootList.add(dto);
            }
        }
        Collections.sort(rootList, aclModuleSeqComparator);
        transformAclModuleTree(rootList, LevelUtil.ROOT, levelDTOMultimap);
        return rootList;
    }

    /**
     * 递归生成权限模块树，每一层都会写到上一层结点中
     * 以ROOT为例，首先遍历rootList，取出每个元素下一级的层级level，
     * 然后同样的如rootList先排序再针对下一层的tempList进行transformAclModuleTree
     * level : 0 -> 0.1, 0.2
     * level : 0.1
     * level : 0.2
     *
     * @param dtoList           当前结构
     * @param level             当前level，这边最开始是以ROOT处理，到了下一层级每次更新
     * @param levelAclModuleMap 存储若干同一级的map，用以辅助计算
     */
    public void transformAclModuleTree(List<AclModuleLevelDTO> dtoList,
                                       String level, Multimap<String,
            AclModuleLevelDTO> levelAclModuleMap) {
        for (int i = 0; i < dtoList.size(); i++) {
            AclModuleLevelDTO dto = dtoList.get(i);
            String nextLevel = LevelUtil.calculateLevel(level, dto.getId());// 计算下一层level
            List<AclModuleLevelDTO> tempList = (List<AclModuleLevelDTO>) levelAclModuleMap.get(nextLevel);
            if (CollectionUtils.isNotEmpty(tempList)) {
                Collections.sort(tempList, aclModuleSeqComparator);
                dto.setAclModuleLevelDTOList(tempList);
                transformAclModuleTree(tempList, nextLevel, levelAclModuleMap);
            }
        }
    }

    /**
     * 权限模块排序规则比较器
     */
    public Comparator<AclModuleLevelDTO> aclModuleSeqComparator = new Comparator<AclModuleLevelDTO>() {
        @Override
        public int compare(AclModuleLevelDTO o1, AclModuleLevelDTO o2) {
            return o1.getSeq() - o2.getSeq();
        }
    };

    /**
     * 权限点排序规则比较器
     */
    public Comparator<AclDTO> aclSeqComparator = new Comparator<AclDTO>() {
        @Override
        public int compare(AclDTO o1, AclDTO o2) {
            return o1.getSeq() - o2.getSeq();
        }
    };
}
